package fr.aracore.domotique.zapi.model;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;

import fr.aracore.domotique.zapi.db.DBManager;
import fr.aracore.domotique.zapi.event.ZbEvent;
import fr.aracore.domotique.zapi.model.impl.SensorDescriptor;

public class Sensor {

	static {
		loadSensorsDescriptor();
	}

	// public static final String[]
	// TAGS={"tem","hum","bat","kwh","kw","tra","cra","uvl","awi","drt","temc","sta","flag3","xse"};
	// public static final String[]
	// TAGS_LIB={"temp","humidity","battery","energy","power","raintotal","rainrate","uv","speed","direction","setpoint","status","status","X10sec"};

	public String tag, value;
	private Device device;

	public static String descriptionForTag(String tag) {
		SensorDescriptor lcSD = sensorDescriptorForTag(tag);
		return lcSD.libelle();
	}

	public Sensor(Device device, String tag, String value) {
		this.tag = tag;
		this.value = value;
		this.device = device;
	}

	public String tag() {
		return tag;
	}

	public String id() {
		return device.idDevice() + "#" + tag();
	}

	public String toString() {
		return "<Sensor " + id() + ">" + descriptionForTag(tag()) + " :"
				+ stateDescription();
	}

	public String value() {
		return value;
	}
	public void updateValue(String value){
		this.value=value;
	}
	public Device device(){
		return device;
	}

	private SensorDescriptor sensorDescriptor() {
		return sensorDescriptorForTag(tag());
	}

	public String stateDescription() {
		return sensorDescriptor().stateDescriptionForSensor(this);
	}

	public static Object[] tags() {
		return (Object[]) sensorDescriptors.keySet().toArray();
	}

	public static void addSensorsToDeviceWithEvent(Device device, ZbEvent event) {
		String lcMessage = event.response.message;
		for (int i = 0; i < tags().length; i++) {
			String lcValue = contentForTag(lcMessage, (String)tags()[i]);
			if (lcValue != null){
				Sensor lcSensor=new Sensor(device, (String)tags()[i],lcValue);
				device.addSensor(lcSensor);
				//DB
				if (DBManager.dbManager().dbStorage()){
					try {
						DBManager.dbManager().updateSensor(lcSensor);
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	public static void updateDeviceWithEvent(Device device, ZbEvent event) {
		String lcMessage = event.response.message;
		for (int i = 0; i < tags().length; i++) {
			String lcValue = contentForTag(lcMessage, (String)tags()[i]);
			if (lcValue != null){
				Sensor lcSensor=device.getSensor((String)tags()[i]);
				lcSensor.updateValue(lcValue);
				//DB
				if (DBManager.dbManager().dbStorage()){
					try {
						DBManager.dbManager().updateSensor(lcSensor);
					} catch (SQLException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	private static String contentForTag(String message, String tag) {
		if (message.contains("<"+tag+">")) {
			int lcIndexDebut = message.indexOf("<" + tag + ">")
					+ ("<" + tag + ">").length();
			int lcIndexFin = message.indexOf("</" + tag + ">");
			return message.substring(lcIndexDebut, lcIndexFin);
		}
		return null;
	}

	private static Hashtable<String, SensorDescriptor> sensorDescriptors;

	private static void registerSensorDescriptorForTag(
			SensorDescriptor sensorDescriptor, String tag) {
		if (sensorDescriptors==null) 
			sensorDescriptors=new Hashtable<String, SensorDescriptor>();
		sensorDescriptors.put(tag, sensorDescriptor);
	}

	private static SensorDescriptor sensorDescriptorForTag(String tag) {
		return sensorDescriptors.get(tag);
	}

	private static void loadSensorsDescriptor() {
		String packageName =  SensorDescriptor.class.getPackage().getName();
		try {
			Class[] lcClasses = getClasses(packageName);
			for (int i = 0; i < lcClasses.length; i++) {
				if (lcClasses[i]!=SensorDescriptor.class){
					SensorDescriptor lcSensorDescriptor = (SensorDescriptor) lcClasses[i].newInstance();
					registerSensorDescriptorForTag(lcSensorDescriptor,lcSensorDescriptor.tag());
				}
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Scans all classes accessible from the context class loader which belong
	 * to the given package and subpackages.
	 * 
	 * @param packageName
	 *            The base package
	 * @return The classes
	 * @throws ClassNotFoundException
	 * @throws IOException
	 */
	private static Class[] getClasses(String packageName)
			throws ClassNotFoundException, IOException {
		ClassLoader classLoader = Thread.currentThread()
				.getContextClassLoader();
		assert classLoader != null;
		String path = packageName.replace('.', '/');
		Enumeration<URL> resources = classLoader.getResources(path);
		List<File> dirs = new ArrayList<File>();
		while (resources.hasMoreElements()) {
			URL resource = resources.nextElement();
			dirs.add(new File(resource.getFile()));
		}
		ArrayList<Class> classes = new ArrayList<Class>();
		for (File directory : dirs) {
			classes.addAll(findClasses(directory, packageName));
		}
		return classes.toArray(new Class[classes.size()]);
	}

	/**
	 * Recursive method used to find all classes in a given directory and
	 * subdirs.
	 * 
	 * @param directory
	 *            The base directory
	 * @param packageName
	 *            The package name for classes found inside the base directory
	 * @return The classes
	 * @throws ClassNotFoundException
	 */
	private static List<Class> findClasses(File directory, String packageName)
			throws ClassNotFoundException {
		List<Class> classes = new ArrayList<Class>();
		if (!directory.exists()) {
			return classes;
		}
		File[] files = directory.listFiles();
		for (File file : files) {
			if (file.isDirectory()) {
				assert !file.getName().contains(".");
				classes.addAll(findClasses(file,
						packageName + "." + file.getName()));
			} else if (file.getName().endsWith(".class")) {
				classes.add(Class.forName(packageName
						+ '.'
						+ file.getName().substring(0,
								file.getName().length() - 6)));
			}
		}
		return classes;
	}
	public String idSensor(){
		return device().idDevice()+"-"+tag();
	}
}
